""" An HMAC-Based One-Time Password Algorithm

Based on RFC4226. Copyright (C) 2012  ouwenxing (ouwenxing@gmail.com)

Example:

>>> from pyhotp import HOTP

"""

import array
import hashlib
import hmac

__all__ = ["HOTP"]

# Default secret is form RFC4226 Appendix D
DEFAULT_SECRET = "12345678901234567890"

# The default number of digits in an HOPT value.
DEFAULT_DIGITS = 6
MIN_DIGITS = 6
MAX_DIGITS = 8

DIGITS_POWER = (1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000)

SUPPORT_HASHALG = {"md5":hashlib.md5, 
                   "sha1":hashlib.sha1, 
                   "sha256":hashlib.sha256, 
                   "sha384":hashlib.sha384, 
                   "sha512":hashlib.sha512
                   }


class HOTP:
    """an HOTP class
    
    """
    secret = DEFAULT_SECRET
    digits = DEFAULT_DIGITS
    hashname = "sha1"      # default hash alg.

    def __init__(self, hashname = "sha1", 
                 secret=DEFAULT_SECRET, digits=DEFAULT_DIGITS):

        hashname = hashname.lower()        
        if hashname not in SUPPORT_HASHALG.keys():
            raise ValueError('unsupported hash type %s' % hashname)
        self.hashname = hashname

        self.secret = secret
        
        if digits not in (6, 7, 8):
            raise ValueError('unsupported digits number %d' % digits)
        self.digits = digits
        

    def generateOTP(self, counter):
        """ 
        """
        acounter = array.array('B', [0, 0, 0, 0, 0, 0, 0, 0])
        for i in range(0, 8):
            acounter[7-i] = counter & 0xFF
            counter >>= 8

        hashotp = hmac.new(self.secret, '', SUPPORT_HASHALG[self.hashname])
        hashotp.update(acounter)

        hashdata = hashotp.hexdigest()
        offset = int(hashdata[len(hashdata)-1], 16)
        
        binary = int(hashdata[offset*2 : offset*2+8], 16)
        
        otp = (binary & 0x7FFFFFFF) % DIGITS_POWER[self.digits]

        return otp
    
    def generateStrOTP(self ,counter):

        return str(self.generateOTP(counter))

if __name__ == "__main__":
    myhotp = HOTP()
    otp1 = myhotp.generateOTP(1)
    otp2 = myhotp.generateStrOTP(1)

    print otp1, otp2







        

    
